package deltaDataSyncSys

import (
	"gitee.com/kingzyt/common/log"
)

const (
	DDSS_changeType_add    int32 = 0
	DDSS_changeType_remove int32 = 1
	DDSS_changeType_modify int32 = 2
)

type DDSSData interface {
	GetId() int64
}
type DDSSDataList interface {
	Len() int
	Append(d DDSSData)
	Delete(idx int)
	Set(idx int, d DDSSData)
	Get(idx int) DDSSData
}
type DDSSChange interface {
	GetChangeType() int32
	GetId() int64
	GetData() DDSSData
}
type DDSSChangeList interface {
	Len() int
	Set(idx int, d DDSSChange)
	Get(idx int) DDSSChange
}

//////////////////
type DDSSInterChange interface {
	GetChangeType() int32
	GetId() int64
}
type DDSSInterChangeList interface {
	Len() int
	Set(idx int, d DDSSInterChange)
	Get(idx int) DDSSInterChange
	Append(d DDSSInterChange)
	//GetSlice(startIdx int, endIdx int) DDSSInterChangeList
}

/////////////////////////
type DefaultDDSSInterChange struct {
	ChangeType int32
	Id         int64
}

func (self *DefaultDDSSInterChange) GetChangeType() int32 {
	return self.ChangeType
}
func (self *DefaultDDSSInterChange) GetId() int64 {
	return self.Id
}
func newDefaultInterChange(changeType int32, id int64) DDSSInterChange {
	return &DefaultDDSSInterChange{ChangeType: changeType, Id: id}
}

type DefaultDDSSInterChangeList struct {
	list []*DefaultDDSSInterChange
}

func (self *DefaultDDSSInterChangeList) Len() int {
	return len(self.list)
}
func (self *DefaultDDSSInterChangeList) Set(idx int, d DDSSInterChange) {
	change, ok := d.(*DefaultDDSSInterChange)
	if !ok {
		log.Error("DefaultDDSSInterChangeList", "Set: d(%T) should be DefaultDDSSInterChange type", d)
		return
	}
	self.list[idx] = change
}
func (self *DefaultDDSSInterChangeList) Get(idx int) DDSSInterChange {
	return self.list[idx]
}
func (self *DefaultDDSSInterChangeList) Append(d DDSSInterChange) {
	change, ok := d.(*DefaultDDSSInterChange)
	if !ok {
		log.Error("DefaultDDSSInterChangeList", "Append: d(%T) should be DefaultDDSSInterChange type", d)
		return
	}
	self.list = append(self.list, change)
}

/*func (self *DefaultDDSSInterChangeList) GetSlice(startIdx int, endIdx int) DDSSInterChangeList {
	return &DefaultDDSSInterChangeList{list: self.list[startIdx:endIdx]}
}*/
func newDefaultInterChangeList(size int) DDSSInterChangeList {
	return &DefaultDDSSInterChangeList{list: make([]*DefaultDDSSInterChange, size)}
}

/*
we do not need fetch an old version,
we just sync all client to the newest version,
so changes & datas just store the data current needed,
old change will be ignored when merging changed history
*/
type DeltaDataSyncServer struct {
	baseVer int64
	slave   bool

	changes DDSSInterChangeList
	datas   map[int64]DDSSData

	////////////////////
	NewDataChange     func(changeType int32, id int64, data DDSSData) DDSSChange
	NewDDSSDataList   func(size int) DDSSDataList
	NewDDSSChangeList func(size int) DDSSChangeList

	// optional to custom interchange struct, especially for slave, e.g. we should use interchange proto struct for going though network
	NewDDSSInterChange     func(changeType int32, id int64) DDSSInterChange
	NewDDSSInterChangeList func(size int) DDSSInterChangeList
}

func (self *DeltaDataSyncServer) BaseVer() int64 {
	return self.baseVer
}

////test///////
func (self *DeltaDataSyncServer) Datas() map[int64]DDSSData {
	return self.datas
}
func (self *DeltaDataSyncServer) Changes() DDSSInterChangeList {
	return self.changes
}

/////////

func (self *DeltaDataSyncServer) Initialize(slave bool) {
	if self.NewDDSSInterChange == nil {
		self.NewDDSSInterChange = newDefaultInterChange
		self.NewDDSSInterChangeList = newDefaultInterChangeList
	}
	self.slave = slave
	self.changes = self.NewDDSSInterChangeList(0)
	self.datas = map[int64]DDSSData{}
}

func (self *DeltaDataSyncServer) Len() int {
	return len(self.datas)
}

func (self *DeltaDataSyncServer) checkIsNotSlave(opName string) {
	if self.slave {
		log.Panic("ddss", "this op is not valid for slave, op: %s", opName)
	}
}

func (self *DeltaDataSyncServer) Clear(baseVer int64, clearProc func(d DDSSData)) {
	self.checkIsNotSlave("Clear")

	self.baseVer = baseVer

	if self.changes.Len() > 0 {
		self.changes = self.NewDDSSInterChangeList(0)
	}

	if len(self.datas) > 0 {
		if clearProc != nil {
			for _, v := range self.datas {
				clearProc(v)
			}
		}
		self.datas = map[int64]DDSSData{}
	}
}
func (self *DeltaDataSyncServer) InitAdd(data DDSSData) {
	self.checkIsNotSlave("InitAdd")

	self.datas[data.GetId()] = data
}

func (self *DeltaDataSyncServer) Ver() int64 {
	return self.baseVer + int64(self.changes.Len())
}

func (self *DeltaDataSyncServer) Add(d DDSSData) (updated bool, ver int64) {
	self.checkIsNotSlave("Add")

	changeType := DDSS_changeType_add
	_, updated = self.datas[d.GetId()]
	if updated {
		changeType = DDSS_changeType_modify
	}

	self.changes.Append(self.NewDDSSInterChange(changeType, d.GetId()))
	self.datas[d.GetId()] = d

	return updated, self.Ver()
}
func (self *DeltaDataSyncServer) Remove(id int64) (ok bool, deleted DDSSData, ver int64) {
	self.checkIsNotSlave("Remove")

	deleted, ok = self.datas[id]
	if ok {
		self.changes.Append(self.NewDDSSInterChange(DDSS_changeType_remove, id))
		delete(self.datas, id)

		return true, deleted, self.Ver()
	}

	return false, nil, 0
}
func (self *DeltaDataSyncServer) Contains(id int64) bool {
	_, ok := self.datas[id]
	return ok
}

/*
interchange is const after set, never change
updating data, use new data to replace old data, old data is not changed,
removing data, remove old data from map, old data is not changed,
then changeList & dataList for return are all new list, so all return data is const after call, wont change*/
func (self *DeltaDataSyncServer) GetUpdatesForSlave(slaveBaseVer int64, slaveVer int64) (fullUpdate bool, changes DDSSInterChangeList, datas DDSSDataList, baseVer int64, curVer int64) {
	if slaveBaseVer != self.baseVer || (slaveVer < self.baseVer || self.Ver() < slaveVer) {
		changes = self.NewDDSSInterChangeList(self.changes.Len())
		clen := self.changes.Len()
		for i := 0; i < clen; i++ {
			changes.Set(i, self.changes.Get(i))
		}

		datas = self.NewDDSSDataList(len(self.datas))
		idx := 0
		for _, d := range self.datas {
			datas.Set(idx, d)
			idx++
		}
		return true, changes, datas, self.baseVer, self.Ver()
	}

	offset := int(slaveVer - self.baseVer)

	// slaveVer is the newest, no need update
	changeLen := self.changes.Len()
	if offset >= changeLen {
		return false, nil, nil, self.baseVer, self.Ver()
	}

	changes = self.NewDDSSInterChangeList(changeLen - offset)
	temp := map[int64]byte{}
	addCount := 0
	for i := changeLen - 1; i >= offset; i-- {
		change := self.changes.Get(i)
		changes.Set(i-offset, change)

		changeType := change.GetChangeType()
		id := change.GetId()
		_, ok := temp[id]
		if !ok {
			switch changeType {
			case DDSS_changeType_add, DDSS_changeType_modify:
				temp[id] = 1
				addCount++
			case DDSS_changeType_remove:
				temp[id] = 0
			}
		}
	}

	// fill delta datas
	datas = self.NewDDSSDataList(addCount)
	idx := 0
	for id, send := range temp {
		if send == 1 {
			data, ok := self.datas[id]
			if ok {
				datas.Set(idx, data)
			}
			idx++
		}
	}

	return false, changes, datas, self.baseVer, self.Ver()
}

/*
merge also doesnot change the interchange data & ddssdata, only list will be changed, elems are const
*/
func (self *DeltaDataSyncServer) MergeUpdatesAsSlave(fullUpdate bool, changes DDSSInterChangeList, datas DDSSDataList, baseVer int64, curVer int64) (changed bool) {
	if baseVer == self.baseVer && curVer == self.Ver() {
		return false
	}

	if !fullUpdate && (changes == nil || changes.Len() == 0) && (datas == nil || datas.Len() == 0) {
		return false
	}

	self.baseVer = baseVer

	if fullUpdate {
		if changes != nil {
			self.changes = changes
		} else {
			if self.changes.Len() > 0 {
				self.changes = self.NewDDSSInterChangeList(0)
			}
		}

		if len(self.datas) > 0 {
			self.datas = map[int64]DDSSData{}
		}
	} else {
		if changes != nil {
			temp := map[int64]byte{}

			clen := changes.Len()
			for i := 0; i < clen; i++ {
				self.changes.Append(changes.Get(i))
			}
			for i := clen - 1; i >= 0; i-- {
				change := changes.Get(i)
				id := change.GetId()
				_, ok := temp[id]
				if !ok {
					switch change.GetChangeType() {
					case DDSS_changeType_add, DDSS_changeType_modify:
						temp[id] = 1
					case DDSS_changeType_remove:
						temp[id] = 0
					}
				}
			}

			for id, op := range temp {
				if op == 0 {
					delete(self.datas, id)
				}
			}
		}
	}

	if datas != nil {
		dlen := datas.Len()
		for i := 0; i < dlen; i++ {
			data := datas.Get(i)
			self.datas[data.GetId()] = data
		}
	}

	if self.Ver() != curVer {
		log.Error("ddss", "ver does not match after merge, want:%d, cur:%d", curVer, self.Ver())
	}

	return true
}

/*
master与slave之间同步数量不会很多,而slave/master与client的同步是非常频繁的,
所以末端发送节点必须考虑内存管理,发送步骤所产生的数据都要考虑回收,
changes的所有元素和列表本身都是可回收的,而datas只有列表本身是可回收的
*/
func (self *DeltaDataSyncServer) GetUpdates(clientVer int64) (fullUpdate bool, changes DDSSChangeList, datas DDSSDataList, ver int64) {
	if clientVer < self.baseVer || self.Ver() < clientVer {
		datas = self.NewDDSSDataList(len(self.datas))
		idx := 0
		for _, d := range self.datas {
			datas.Set(idx, d)
			idx++
		}
		return true, nil, datas, self.Ver()
	}

	/*
		差值指向cver的下一个版本，
		base5，change len10，cver8，则offset为3，用作idx指向第4项，6,7,8,[9]，
		若cver为最新，则超出change范围，判定为无需更新。

		若cver与basever一样,则offset为0,指向change的第一个元素,ok
	*/
	offset := int(clientVer - self.baseVer)

	// clientVer is the newest, no need update
	changeLen := self.changes.Len()
	if offset >= changeLen {
		return false, nil, nil, self.Ver()
	}

	temp := map[int64]DDSSChange{}
	for i := changeLen - 1; i >= offset; i-- {
		change := self.changes.Get(i)
		changeType := change.GetChangeType()
		id := change.GetId()
		_, ok := temp[id]
		// just take the newest change
		if !ok {
			switch changeType {
			case DDSS_changeType_add, DDSS_changeType_modify:
				data, existed := self.datas[id]
				if existed {
					c := self.NewDataChange(changeType, id, data)
					if c != nil {
						temp[id] = c
					}
				}
			case DDSS_changeType_remove:
				c := self.NewDataChange(changeType, id, nil)
				if c != nil {
					temp[id] = c
				}
			}
		}
	}

	changes = self.NewDDSSChangeList(len(temp))
	idx := 0
	for _, c := range temp {
		changes.Set(idx, c)
		idx++
	}

	return false, changes, nil, self.Ver()
}

/*
初衷是为了方便客户端分发等后续操作,
ddssdatalist直接为proto结构,这样发送的时候可以直接调用marshal,不用再手填一个proto结构
低频率S更改,高频率C后续分发
*/
type DeltaDataSyncClient struct {
	datas DDSSDataList
	ver   int64

	NewDDSSDataList func(size int) DDSSDataList
}

func (self *DeltaDataSyncClient) Initialize() {
	self.datas = self.NewDDSSDataList(0)
}

func (self *DeltaDataSyncClient) Ver() int64 {
	return self.ver
}

func (self *DeltaDataSyncClient) Datas() DDSSDataList {
	return self.datas
}

// 已经考虑了同一个版本中,重复发送pull的时候,相同数据的res进行多次重复merge调用的情况,不会因为多次merge而造成数据错误
func (self *DeltaDataSyncClient) MergeUpdates(fullUpdate bool, changes DDSSChangeList, datas DDSSDataList, ver int64) {
	self.ver = ver
	if !fullUpdate && (changes == nil || changes.Len() == 0) && (datas == nil || datas.Len() == 0) {
		return
	}

	if fullUpdate {
		if datas != nil {
			self.datas = datas
		} else {
			self.datas = self.NewDDSSDataList(0)
		}
	} else {
		if changes == nil {
			return
		}

		for k := 0; k < changes.Len(); k++ {
			change := changes.Get(k)
			switch change.GetChangeType() {
			case DDSS_changeType_add, DDSS_changeType_modify:
				updated := false
				// we should do full update first(client init ver should be 0), otherwise self.datas will be nil here
				dlen := self.datas.Len()
				for i := 0; i < dlen; i++ {
					if self.datas.Get(i).GetId() == change.GetId() {
						self.datas.Set(i, change.GetData())
						updated = true
						break
					}
				}
				if !updated {
					self.datas.Append(change.GetData())
				}

			case DDSS_changeType_remove:
				dlen := self.datas.Len()
				for i := 0; i < dlen; i++ {
					if self.datas.Get(i).GetId() == change.GetId() {
						self.datas.Delete(i)
						break
					}
				}
			}
		}

	}
}

/*
高频率S更改,低频率C后续分发,多本地直接使用
*/
type DeltaDataSyncClient2 struct {
	datas map[int64]DDSSData
	ver   int64
}

func (self *DeltaDataSyncClient2) Initialize() {
	self.datas = map[int64]DDSSData{}
}

func (self *DeltaDataSyncClient2) Ver() int64 {
	return self.ver
}

func (self *DeltaDataSyncClient2) Datas() map[int64]DDSSData {
	return self.datas
}

func (self *DeltaDataSyncClient2) MergeUpdates(fullUpdate bool, changes DDSSChangeList, datas DDSSDataList, ver int64) {
	self.ver = ver
	if !fullUpdate && (changes == nil || changes.Len() == 0) && (datas == nil || datas.Len() == 0) {
		return
	}

	if fullUpdate {
		if datas != nil {
			self.datas = map[int64]DDSSData{}
			dlen := datas.Len()
			for i := 0; i < dlen; i++ {
				data := datas.Get(i)
				self.datas[data.GetId()] = data
			}
		} else {
			self.datas = map[int64]DDSSData{}
		}
	} else {
		if changes == nil {
			return
		}

		clen := changes.Len()
		for i := 0; i < clen; i++ {
			change := changes.Get(i)
			switch change.GetChangeType() {
			case DDSS_changeType_add, DDSS_changeType_modify:
				self.datas[change.GetId()] = change.GetData()
			case DDSS_changeType_remove:
				_, ok := self.datas[change.GetId()]
				if ok {
					delete(self.datas, change.GetId())
				}
			}
		}
	}
}

// default ddss data structs///////////////////////////////////////////////////
/*
type defaultDDSSData struct {
	Id   int64
	Data interface{}
}

func (self *defaultDDSSData) GetId() int64 {
	return self.Id
}

type defaultDDSSDataList struct {
	datas []*defaultDDSSData
}

func (self *defaultDDSSDataList) Len() int {
	return len(self.datas)
}
func (self *defaultDDSSDataList) Set(idx int, d DDSSData) {
	self.datas[idx] = d.(*defaultDDSSData)
}
func (self *defaultDDSSDataList) Get(idx int) DDSSData {
	return self.datas[idx]
}
func (self *defaultDDSSDataList) Append(d DDSSData) {
	self.datas = append(self.datas, d.(*defaultDDSSData))
}
func (self *defaultDDSSDataList) Delete(idx int) {
	cursor := 0
	for i := 0; i < len(self.datas); i++ {
		if i == idx {
			continue
		} else {
			if i > cursor {
				self.datas[cursor] = self.datas[i]
			}
			cursor++
		}
	}

	self.datas = self.datas[:cursor]
}

type defaultDDSSDataChange struct {
	ChangeType int32
	Id         int64
	Data       *defaultDDSSData
}

func (self *defaultDDSSDataChange) GetChangeType() int32 {
	return self.ChangeType
}
func (self *defaultDDSSDataChange) GetId() int64 {
	return self.Id
}
func (self *defaultDDSSDataChange) GetData() DDSSData {
	return self.Data
}

type defaultDDSSDataChangeList struct {
	datas []*defaultDDSSDataChange
}

func (self *defaultDDSSDataChangeList) Len() int {
	return len(self.datas)
}
func (self *defaultDDSSDataChangeList) Set(idx int, d DDSSChange) {
	self.datas[idx] = d.(*defaultDDSSDataChange)
}
func (self *defaultDDSSDataChangeList) Get(idx int) DDSSChange {
	return self.datas[idx]
}

func newDefaultDDSSDataChange(changeType int32, id int64, data DDSSData) DDSSChange {
	d := &defaultDDSSDataChange{}
	d.ChangeType = changeType
	d.Id = id
	if data != nil {
		dd, ok := data.(*defaultDDSSData)
		if !ok {
			return nil
		}
		d.Data = dd
	} else {
		d.Data = nil
	}
	return d
}
func newDefaultDDSSDataList(size int) DDSSDataList {
	return &defaultDDSSDataList{datas: make([]*defaultDDSSData, size)}
}
func newDefaultDDSSDataChangeList(size int) DDSSChangeList {
	return &defaultDDSSDataChangeList{datas: make([]*defaultDDSSDataChange, size)}
}

func NewDefaultDDSSServer(slave bool) *DeltaDataSyncServer {
	ddsss := &DeltaDataSyncServer{}
	ddsss.NewDataChange = newDefaultDDSSDataChange
	ddsss.NewDDSSDataList = newDefaultDDSSDataList
	ddsss.NewDDSSChangeList = newDefaultDDSSDataChangeList
	ddsss.Initialize(slave)
	return ddsss
}
func NewDefaultDDSSClient() *DeltaDataSyncClient {
	ddssc := &DeltaDataSyncClient{}
	ddssc.NewDDSSDataList = newDefaultDDSSDataList
	ddssc.Initialize()
	return ddssc
}
*/
